/**
 * 
 */
package serialisierung;

import helpers.IDConverter;
import helpers.PriceConverter;

import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import model.Product;

import com.thoughtworks.xstream.XStream;

import fpt.com.SerializableStrategy;

public class XStreamStrategy implements SerializableStrategy {

	private static final String XSTREAM_FILE = "xstream-products.xml";
	private FileInputStream fis;
	private XStream xStream;
	private FileWriter fw;
	private ObjectInputStream objInStr;
	private ObjectOutputStream objOutStr;

	/**
	 * 4a) Attribute werden erstellt, indem der jeweiligen Klasse (in unserem Fall Product)
	 * weitere Instanzvariablen hinzugefügt werden. Wenn z.B. "private String beschreibung;"
	 * hinzugefügt wird, so enthält das XML Format für jedes Product einen Tag <beschreibung>
	 * (vorausgesetzt die Variable wird z.B. durch setBeschreibung("...") gefüllt)
	 * 
	 * Eigenschaften werden mit xstream.aliasField(String alias, Class definedIn, String fieldName);
	 * umbenannt. alias gibt an, wie die Eigenschaft im XML-Format heißen soll, definiedIn gibt die
	 * Klasse an, in der die Eigenschaft definiert ist (z.B. Product.class) und fieldName gibt
	 * den Namen der Variable an (z.B. "price")
	 * 
	 * Der Tag für eine Instanz (z.B. model.Product) kann mit xstream.alias("ware", Product.class)
	 * zu "ware" umbenannt werden.
	 * 
	 * 4b)
	 * Objekte können durch Konverter in der gewünschten Form dargestellt werden
	 * Ein SingleValueConverter dient z.B. dazu, ein bestimmtes Feld wie gewünscht
	 * zu konventieren (z.B. um aus einer ID '6' die ID '000006' zu machen)
	 */

	public XStreamStrategy() {
		xStream = new XStream();

		// Konverter dienen dazu, die ID und den Preis in der gewünschten Form darzustellen
		// Die Konverter werden hier nur für das jeweilige Feld registriert
		xStream.registerLocalConverter(Product.class, "id", new IDConverter());
		xStream.registerLocalConverter(Product.class, "price",
				new PriceConverter());

		// XML-Tag <model.Product> wird zu <ware> umbenannt
		xStream.alias("ware", Product.class);

		// Felder von Product werden in XML wie angegeben benannt
		xStream.aliasField("preis", Product.class, "price");
		xStream.aliasField("anzahl", Product.class, "quantity");

		// ID wird als Attribut für das Product benutzt, d.h.
		// statt <ware> <id>123456</id> ... </ware> wird
		// <ware id=123456> ... </ware> angezeigt
		xStream.useAttributeFor(Product.class, "id");

	}

	/*
	 * Liest Product aus XML ein.
	 */
	@Override
	public Product readObject() throws IOException {

		if (fis == null) {
			// Neue FileInputStream aus zu ladender Datei erstellen
			fis = new FileInputStream(XSTREAM_FILE);
			objInStr = xStream.createObjectInputStream(fis); // Wird zum Einlesen benötigt
		}

		try {
			return (Product) objInStr.readObject();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			System.out.println("Kein gültiges Objekt");
			return null;
		}

	}

	/*
	 * Hier werden Input- und Outputstreams sowie Writer geschlossen
	 */
	@Override
	public void close() throws IOException {

		if (objOutStr != null) {
			objOutStr.close();
			objOutStr = null;
		}

		if (objInStr != null) {
			objInStr.close();
			objInStr = null;
		}

		if (fw != null) {
			fw.close();
			fw = null;
		}

		if (fis != null) {
			fis.close();
			fis = null;
		}

	}

	@Override
	public void writeObject(fpt.com.Product prod) throws IOException {

		if (fw == null) {
			// FileWriter erstellen, falls nicht vorhanden
			fw = new FileWriter(XSTREAM_FILE);

			// ObjectOutputStream wird benötigt, um XML zu schreiben
			objOutStr = xStream.createObjectOutputStream(fw, "waren");
			// "waren" ist root node - d.h. <waren> steht am Anfang der XML
			// bzw. </waren> am Ende
		}

		objOutStr.writeObject(prod); // weiteres Produkt in XML schreiben

	}
}
